<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>调用链追踪可视化</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.8/dist/chart.umd.min.js"></script>
    <script>
        // Tailwind自定义配置
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#165DFF',
                        success: '#00B42A',
                        danger: '#F53F3F',
                        warning: '#FF7D00',
                        info: '#86909C',
                        'tree-line': '#C9CDD4',
                        'card-bg': '#FFFFFF',
                        'page-bg': '#F2F3F5'
                    },
                    fontFamily: {
                        inter: ['Inter', 'system-ui', 'sans-serif'],
                    },
                    boxShadow: {
                        'card': '0 2px 8px 0 rgba(0, 0, 0, 0.08)',
                        'card-hover': '0 4px 16px 0 rgba(0, 0, 0, 0.12)',
                    }
                },
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }

            .node-hover {
                @apply transition-all duration-200 hover:shadow-card-hover hover:translate-y-[-2px];
            }

            .scrollbar-thin {
                scrollbar-width: thin;
            }

            .scrollbar-thin::-webkit-scrollbar {
                width: 6px;
                height: 6px;
            }

            .scrollbar-thin::-webkit-scrollbar-thumb {
                background-color: rgba(156, 163, 175, 0.5);
                border-radius: 3px;
            }
        }
    </style>
</head>
<body class="bg-page-bg font-inter text-dark min-h-screen flex flex-col">
<!-- 顶部导航 -->
<header class="bg-white shadow-sm sticky top-0 z-50">
    <div class="container mx-auto px-4 py-3 flex items-center justify-between">
        <div class="flex items-center space-x-2">
            <i class="fa fa-sitemap text-primary text-2xl"></i>
            <h1 class="text-xl font-semibold text-dark">调用链追踪可视化</h1>
        </div>
        <div class="flex items-center space-x-4">
            <div class="relative hidden md:block">
                <input type="text" id="globalSearch" placeholder="搜索traceId、方法名或SQL..."
                       class="pl-9 pr-4 py-2 rounded-lg border border-gray-200 focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition-all">
                <i class="fa fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
            </div>
            <button id="refreshBtn" class="p-2 rounded-lg hover:bg-gray-100 transition-colors" title="刷新数据">
                <i class="fa fa-refresh text-gray-600"></i>
            </button>
            <button id="fullscreenBtn" class="p-2 rounded-lg hover:bg-gray-100 transition-colors" title="全屏查看">
                <i class="fa fa-expand text-gray-600"></i>
            </button>
        </div>
    </div>
</header>

<!-- 主内容区 -->
<main class="flex-1 container mx-auto px-4 py-6 grid grid-cols-1 lg:grid-cols-4 gap-6">
    <!-- 左侧统计面板 -->
    <div class="lg:col-span-1 space-y-6">
        <!-- 统计卡片 -->
        <div class="bg-card-bg rounded-xl shadow-card p-5">
            <h2 class="text-lg font-medium mb-4 flex items-center">
                <i class="fa fa-bar-chart text-primary mr-2"></i>调用统计
            </h2>
            <div class="grid grid-cols-2 gap-4">
                <div class="bg-blue-50 rounded-lg p-3">
                    <p class="text-sm text-gray-500">总调用数</p>
                    <p class="text-2xl font-semibold text-primary" id="totalCount">0</p>
                </div>
                <div class="bg-green-50 rounded-lg p-3">
                    <p class="text-sm text-gray-500">成功数</p>
                    <p class="text-2xl font-semibold text-success" id="successCount">0</p>
                </div>
                <div class="bg-red-50 rounded-lg p-3">
                    <p class="text-sm text-gray-500">失败数</p>
                    <p class="text-2xl font-semibold text-danger" id="failCount">0</p>
                </div>
                <div class="bg-orange-50 rounded-lg p-3">
                    <p class="text-sm text-gray-500">平均耗时(ms)</p>
                    <p class="text-2xl font-semibold text-warning" id="avgCost">0</p>
                </div>
            </div>

            <!-- 耗时分布图表 -->
            <div class="mt-6">
                <p class="text-sm text-gray-500 mb-2">耗时分布</p>
                <div class="h-48">
                    <canvas id="costDistributionChart"></canvas>
                </div>
            </div>
        </div>

        <!-- 筛选面板 -->
        <div class="bg-card-bg rounded-xl shadow-card p-5">
            <h2 class="text-lg font-medium mb-4 flex items-center">
                <i class="fa fa-filter text-primary mr-2"></i>筛选条件
            </h2>
            <div class="space-y-4">
                <div>
                    <label class="block text-sm text-gray-600 mb-1">节点类型</label>
                    <div class="flex flex-wrap gap-2">
                        <label class="inline-flex items-center">
                            <input type="checkbox" checked class="form-checkbox rounded text-primary" value="method">
                            <span class="ml-1 text-sm">方法调用</span>
                        </label>
                        <label class="inline-flex items-center">
                            <input type="checkbox" checked class="form-checkbox rounded text-primary" value="sql">
                            <span class="ml-1 text-sm">SQL执行</span>
                        </label>
                    </div>
                </div>
                <div>
                    <label class="block text-sm text-gray-600 mb-1">执行状态</label>
                    <div class="flex flex-wrap gap-2">
                        <label class="inline-flex items-center">
                            <input type="checkbox" checked class="form-checkbox rounded text-success" value="success">
                            <span class="ml-1 text-sm">成功</span>
                        </label>
                        <label class="inline-flex items-center">
                            <input type="checkbox" checked class="form-checkbox rounded text-danger" value="fail">
                            <span class="ml-1 text-sm">失败</span>
                        </label>
                    </div>
                </div>
                <div>
                    <label class="block text-sm text-gray-600 mb-1">耗时范围(ms)</label>
                    <div class="flex items-center gap-2">
                        <input type="number" placeholder="最小"
                               class="flex-1 px-3 py-2 border border-gray-200 rounded-lg text-sm">
                        <span>-</span>
                        <input type="number" placeholder="最大"
                               class="flex-1 px-3 py-2 border border-gray-200 rounded-lg text-sm">
                    </div>
                </div>
                <button id="applyFilter"
                        class="w-full bg-primary text-white py-2 rounded-lg hover:bg-primary/90 transition-colors">
                    应用筛选
                </button>
            </div>
        </div>
    </div>

    <!-- 右侧树形+详情 -->
    <div class="lg:col-span-3 space-y-6">
        <!-- 树形展示区 -->
        <div class="bg-card-bg rounded-xl shadow-card overflow-hidden">
            <div class="p-5 border-b border-gray-100">
                <div class="flex flex-col md:flex-row md:items-center justify-between gap-4">
                    <h2 class="text-lg font-medium flex items-center">
                        <i class="fa fa-tree text-primary mr-2"></i>调用链树形结构
                    </h2>
                    <div class="flex flex-wrap gap-2">
                        <button id="expandAll"
                                class="px-3 py-1 text-sm border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
                            <i class="fa fa-plus-square-o mr-1"></i>全部展开
                        </button>
                        <button id="collapseAll"
                                class="px-3 py-1 text-sm border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
                            <i class="fa fa-minus-square-o mr-1"></i>全部折叠
                        </button>
                        <button id="resetView"
                                class="px-3 py-1 text-sm border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
                            <i class="fa fa-refresh mr-1"></i>重置视图
                        </button>
                    </div>
                </div>
            </div>
            <div class="p-4 max-h-[500px] overflow-y-auto scrollbar-thin" id="treeContainer">
                <!-- 加载状态 -->
                <div class="flex items-center justify-center h-40 text-gray-400" id="loadingState">
                    <div class="text-center">
                        <i class="fa fa-spinner fa-spin text-2xl mb-2"></i>
                        <p>加载调用链数据中...</p>
                    </div>
                </div>
                <!-- 空数据状态 -->
                <div class="flex items-center justify-center h-40 text-gray-400 hidden" id="emptyState">
                    <p>未获取到调用链数据</p>
                </div>
                <!-- 错误状态 -->
                <div class="flex items-center justify-center h-40 text-danger hidden" id="errorState">
                    <div class="text-center">
                        <i class="fa fa-exclamation-circle text-2xl mb-2"></i>
                        <p id="errorMsg">加载数据失败，请重试</p>
                        <button id="retryBtn" class="mt-2 text-primary hover:underline">重试</button>
                    </div>
                </div>
            </div>
        </div>

        <!-- 节点详情面板 -->
        <div class="bg-card-bg rounded-xl shadow-card overflow-hidden" id="detailPanel">
            <div class="p-5 border-b border-gray-100">
                <h2 class="text-lg font-medium flex items-center">
                    <i class="fa fa-info-circle text-primary mr-2"></i>节点详情
                </h2>
            </div>
            <div class="p-5">
                <div class="flex items-center justify-center h-40 text-gray-400" id="emptyDetail">
                    <p>请选择一个节点查看详情</p>
                </div>
                <div class="hidden" id="nodeDetailContent">
                    <!-- 详情内容动态生成 -->
                </div>
            </div>
        </div>
    </div>
</main>

<!-- 页脚 -->
<footer class="bg-white border-t border-gray-200 py-4 mt-6">
    <div class="container mx-auto px-4 text-center text-sm text-gray-500">
        <p>调用链追踪可视化工具 © 2024</p>
    </div>
</footer>

<script>
    // DOM元素缓存
    const dom = {
        treeContainer: document.getElementById('treeContainer'),
        loadingState: document.getElementById('loadingState'),
        emptyState: document.getElementById('emptyState'),
        errorState: document.getElementById('errorState'),
        errorMsg: document.getElementById('errorMsg'),
        retryBtn: document.getElementById('retryBtn'),
        emptyDetail: document.getElementById('emptyDetail'),
        nodeDetailContent: document.getElementById('nodeDetailContent'),
        expandAll: document.getElementById('expandAll'),
        collapseAll: document.getElementById('collapseAll'),
        resetView: document.getElementById('resetView'),
        refreshBtn: document.getElementById('refreshBtn'),
        fullscreenBtn: document.getElementById('fullscreenBtn'),
        globalSearch: document.getElementById('globalSearch'),
        applyFilter: document.getElementById('applyFilter'),
        // 统计元素
        totalCount: document.getElementById('totalCount'),
        successCount: document.getElementById('successCount'),
        failCount: document.getElementById('failCount'),
        avgCost: document.getElementById('avgCost')
    };

    // 全局状态
    let currentTreeData = []; // 接口返回的原始树形数据
    let costDistributionChart = null; // 耗时分布图表实例

    /**
     * 初始化图表
     */
    function initChart() {
        const ctx = document.getElementById('costDistributionChart').getContext('2d');
        costDistributionChart = new Chart(ctx, {
            type: 'bar',
            data: {
                labels: ['0-10ms', '10-50ms', '50-100ms', '100-500ms', '500ms以上'],
                datasets: [{
                    label: '节点数量',
                    data: [0, 0, 0, 0, 0],
                    backgroundColor: [
                        'rgba(22, 93, 255, 0.6)',
                        'rgba(0, 180, 42, 0.6)',
                        'rgba(255, 125, 0, 0.6)',
                        'rgba(245, 63, 63, 0.6)',
                        'rgba(134, 144, 156, 0.6)'
                    ],
                    borderWidth: 0,
                    borderRadius: 4
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                plugins: {legend: {display: false}},
                scales: {y: {beginAtZero: true, ticks: {precision: 0}}}
            }
        });
    }

    /**
     * 从接口获取树形数据
     */
    async function fetchTreeData() {
        try {
            showState('loading');
            let url = window.location.href.replace('/trace-visualization.html', '/api/trace-monitor/stats');
            const response = await fetch(url);
            if (!response.ok) {
                throw new Error(`HTTP错误: 状态码 ${response.status}`);
            }
            const data = await response.json();
            // 校验数据格式（确保是数组，且子节点字段存在）
            if (!Array.isArray(data)) {
                throw new Error('接口返回数据格式错误，应为数组');
            }
            // 初始化子节点字段（防止部分节点没有children）
            const formatTreeData = (nodes) => {
                return nodes.map(node => ({
                    ...node,
                    children: node.children && Array.isArray(node.children) ? formatTreeData(node.children) : []
                }));
            };
            return formatTreeData(data);
        } catch (error) {
            showState('error', error.message);
            return null;
        }
    }

    /**
     * 显示指定状态（loading/empty/error）
     */
    function showState(state, errorMsg = '') {
        // 隐藏所有状态
        ['loading', 'empty', 'error'].forEach(type => {
            dom[`${type}State`].classList.add('hidden');
        });
        // 显示目标状态
        dom[`${state}State`].classList.remove('hidden');
        // 错误信息赋值
        if (state === 'error' && errorMsg) {
            dom.errorMsg.textContent = `加载失败: ${errorMsg}`;
        }
    }

    /**
     * 渲染树形结构（基于接口返回的树形数据）
     * @param {Array} treeData - 接口返回的树形结构数据
     */
    function renderTree(treeData) {
        if (!treeData || treeData.length === 0) {
            showState('empty');
            return;
        }

        // 清空容器并渲染
        dom.treeContainer.innerHTML = '';
        const rootContainer = document.createElement('ul');
        rootContainer.className = 'tree-root pl-0';

        // 递归渲染单个节点（核心：直接使用节点自带的children）
        function renderNode(node, parentElement, depth) {
            // 节点容器
            const nodeItem = document.createElement('li');
            nodeItem.className = 'mb-1 relative node-item';
            nodeItem.dataset.spanId = node.spanId || '';
            nodeItem.dataset.traceId = node.traceId || '';

            // 缩进计算（基于节点深度）
            const indent = depth * 24;

            // 节点内容区
            const nodeContent = document.createElement('div');
            nodeContent.className = `node-content ml-${indent} flex items-center cursor-pointer node-hover rounded-lg p-2`;

            // 节点类型判断（SQL/方法）
            const isSqlNode = !!node.sqlId;
            const iconClass = isSqlNode ? 'fa-database' : 'fa-cube';
            const statusClass = node.success ? 'text-success' : 'text-danger';
            const statusText = node.success ? '成功' : '失败';

            // 展开/折叠按钮（有子节点才显示）
            const children = node.children || [];
            const hasChildren = children.length > 0;
            let toggleBtn = '';
            if (hasChildren) {
                toggleBtn = `
                        <button class="toggle-btn mr-2 text-tree-line hover:text-primary transition-colors" data-span-id="${node.spanId || ''}">
                            <i class="fa fa-caret-right"></i>
                        </button>
                    `;
            } else {
                toggleBtn = '<span class="w-5 inline-block"></span>'; // 占位对齐
            }

            // 节点HTML内容
            //  <span class="font-medium truncate">${isSqlNode ? (node.sqlId?.split('.').pop() || 'SQL执行') : (node.methodName || '未知方法')}</span>
            //${isSqlNode ? 'SQL· ' + node.serviceName : (node.serviceName || '未知服务')} · ${node.costTime || 0}ms
            // <span class="font-medium truncate">${(node.serviceName || '未知类')}==>${node.methodName || '未知方法'}</span>
            nodeContent.innerHTML = `
                    ${toggleBtn}
                    <i class="fa ${iconClass} ${statusClass} mr-2"></i>
                    <div class="flex-1 min-w-0">
                        <div class="flex items-center">
                        <span class="font-medium truncate"> ${(isSqlNode && (node.serviceName == null || node.serviceName === ''))
                ? (node.methodName || 'SQL执行')
                : `${node.serviceName || '未知类'}==>${node.methodName || '未知方法'}`
            }</span>
                            <span class="ml-2 text-xs px-1.5 py-0.5 rounded ${node.success ? 'bg-success/10 text-success' : 'bg-danger/10 text-danger'}">
                                ${statusText}  · ${isSqlNode ? 'SQL ' : ''}
                            </span>
                        </div>
                        <div class="text-xs text-gray-500 truncate">
                         线程：${(node.threadName || '未知线程')} · 耗时：${node.costTime || 0}ms
                        </div>
                    </div>
                `;

            // 节点点击事件（显示详情）
            nodeContent.addEventListener('click', (e) => {
                if (!e.target.closest('.toggle-btn')) { // 排除展开/折叠按钮的点击
                    showNodeDetail(node);
                    // 高亮选中节点
                    document.querySelectorAll('.node-content').forEach(el => {
                        el.classList.remove('bg-primary/5', 'font-medium');
                    });
                    nodeContent.classList.add('bg-primary/5', 'font-medium');
                }
            });

            // 添加节点到父容器
            nodeItem.appendChild(nodeContent);

            // 渲染子节点（直接使用节点自带的children）
            if (hasChildren) {
                const childrenContainer = document.createElement('ul');
                childrenContainer.className = 'children-container hidden pl-4';
                nodeItem.appendChild(childrenContainer);

                // 递归渲染每个子节点
                children.forEach(child => {
                    renderNode(child, childrenContainer, depth + 1);
                });

                // 展开/折叠按钮事件（绑定在当前节点的按钮上）
                const toggleBtnEl = nodeItem.querySelector('.toggle-btn');
                toggleBtnEl.addEventListener('click', (e) => {
                    e.stopPropagation(); // 防止触发节点详情点击
                    const caretIcon = toggleBtnEl.querySelector('i');
                    const isExpanded = caretIcon.classList.contains('fa-caret-down');

                    if (isExpanded) {
                        caretIcon.classList.replace('fa-caret-down', 'fa-caret-right');
                        childrenContainer.classList.add('hidden');
                    } else {
                        caretIcon.classList.replace('fa-caret-right', 'fa-caret-down');
                        childrenContainer.classList.remove('hidden');
                    }
                });
            }

            parentElement.appendChild(nodeItem);
        }

        // 渲染所有根节点
        treeData.forEach(rootNode => {
            renderNode(rootNode, rootContainer, 0);
        });

        dom.treeContainer.appendChild(rootContainer);
        // 更新统计信息
        updateStatistics(treeData);
    }

    /**
     * 显示节点详情
     * @param {Object} node - 选中的节点数据
     */
    function showNodeDetail(node) {
        dom.emptyDetail.classList.add('hidden');
        dom.nodeDetailContent.classList.remove('hidden');

        // 格式化时间戳
        const formatTime = (timestamp) => {
            if (!timestamp) return '-';
            const date = new Date(timestamp);
            return date.toLocaleString('zh-CN', {
                year: 'numeric', month: '2-digit', day: '2-digit',
                hour: '2-digit', minute: '2-digit', second: '2-digit',
                fractionalSecondDigits: 3
            });
        };

        // 判断节点类型
        const isSqlNode = !!node.sqlId;

        // 详情HTML内容
        let detailHtml = `
                <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
                    <div class="bg-gray-50 p-3 rounded-lg">
                        <p class="text-xs text-gray-500 mb-1">节点类型</p>
                        <p class="font-medium">${isSqlNode ? 'SQL执行' : '方法调用'}</p>
                    </div>
                    <div class="bg-gray-50 p-3 rounded-lg">
                        <p class="text-xs text-gray-500 mb-1">执行状态</p>
                        <p class="font-medium ${node.success ? 'text-success' : 'text-danger'}">
                            ${node.success ? '成功' : '失败'}
                        </p>
                    </div>
                    <div class="bg-gray-50 p-3 rounded-lg">
                        <p class="text-xs text-gray-500 mb-1">耗时</p>
                        <p class="font-medium">${node.costTime || 0} ms</p>
                    </div>
                    <div class="bg-gray-50 p-3 rounded-lg">
                        <p class="text-xs text-gray-500 mb-1">调用深度</p>
                        <p class="font-medium">${node.depth || 0}</p>
                    </div>
                    <div class="bg-gray-50 p-3 rounded-lg md:col-span-2">
                        <p class="text-xs text-gray-500 mb-1">开始时间</p>
                        <p class="font-medium">${formatTime(node.startTime)}</p>
                    </div>
                    <div class="bg-gray-50 p-3 rounded-lg md:col-span-2">
                        <p class="text-xs text-gray-500 mb-1">结束时间</p>
                        <p class="font-medium">${formatTime(node.endTime)}</p>
                    </div>
                </div>

                <div class="mb-6">
                    <h3 class="text-sm font-medium mb-2">标识信息</h3>
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                        <div class="bg-gray-50 p-3 rounded-lg">
                            <p class="text-xs text-gray-500 mb-1">traceId</p>
                            <p class="font-mono text-sm truncate hover:whitespace-normal break-all transition-all">${node.traceId || '-'}</p>
                        </div>
                        <div class="bg-gray-50 p-3 rounded-lg">
                            <p class="text-xs text-gray-500 mb-1">spanId</p>
                            <p class="font-mono text-sm truncate hover:whitespace-normal break-all transition-all">${node.spanId || '-'}</p>
                        </div>
                        <div class="bg-gray-50 p-3 rounded-lg md:col-span-2">
                            <p class="text-xs text-gray-500 mb-1">parentSpanId</p>
                            <p class="font-mono text-sm truncate hover:whitespace-normal break-all transition-all">${node.parentSpanId || '-'}</p>
                        </div>
                    </div>
                </div>
            `;

        // 追加SQL或方法特有信息
        if (isSqlNode) {
            detailHtml += `
                    <div class="mb-6">
                        <h3 class="text-sm font-medium mb-2">SQL信息</h3>
                        <div class="bg-gray-50 p-3 rounded-lg mb-4">
                            <p class="text-xs text-gray-500 mb-1">SQL ID</p>
                            <p class="text-sm truncate hover:whitespace-normal break-all transition-all">${node.sqlId || '-'}</p>
                        </div>
                        <div class="mb-4">
                            <p class="text-xs text-gray-500 mb-1">原始SQL（带占位符）</p>
                            <pre class="bg-gray-900 text-gray-100 p-3 rounded-lg text-xs overflow-x-auto scrollbar-thin"><code>${node.originalSql || '-'}</code></pre>
                        </div>
                        <div>
                            <p class="text-xs text-gray-500 mb-1">完整SQL（替换参数后）</p>
                            <pre class="bg-gray-900 text-gray-100 p-3 rounded-lg text-xs overflow-x-auto scrollbar-thin"><code>${node.fullSql || '-'}</code></pre>
                        </div>
                    </div>
                `;
        } else {
            detailHtml += `
                    <div>
                        <h3 class="text-sm font-medium mb-2">方法信息</h3>
                        <div class="bg-gray-50 p-3 rounded-lg mb-4">
                            <p class="text-xs text-gray-500 mb-1">服务名</p>
                            <p class="text-sm">${node.serviceName || '-'}</p>
                        </div>
                        <div class="bg-gray-50 p-3 rounded-lg">
                            <p class="text-xs text-gray-500 mb-1">方法名</p>
                            <p class="text-sm">${node.methodName || '-'}</p>
                        </div>
                    </div>
                `;
        }

        dom.nodeDetailContent.innerHTML = detailHtml;
    }

    /**
     * 更新统计信息（遍历树形数据的所有节点）
     * @param {Array} treeData - 接口返回的树形数据
     */
    function updateStatistics(treeData) {
        // 递归收集所有节点
        const allNodes = [];
        const collectAllNodes = (nodes) => {
            nodes.forEach(node => {
                allNodes.push(node);
                // 递归收集子节点
                if (node.children && Array.isArray(node.children)) {
                    collectAllNodes(node.children);
                }
            });
        };
        collectAllNodes(treeData);

        // 计算统计值
        const totalCount = allNodes.length;
        const successCount = allNodes.filter(n => n.success).length;
        const failCount = totalCount - successCount;
        const totalCost = allNodes.reduce((sum, n) => sum + (n.costTime || 0), 0);
        const avgCost = totalCount > 0 ? Math.round(totalCost / totalCount) : 0;

        // 更新DOM
        dom.totalCount.textContent = totalCount;
        dom.successCount.textContent = successCount;
        dom.failCount.textContent = failCount;
        dom.avgCost.textContent = avgCost;

        // 更新图表数据
        updateChartData(allNodes);
    }

    /**
     * 更新耗时分布图表
     * @param {Array} allNodes - 所有节点的扁平数组
     */
    function updateChartData(allNodes) {
        // 初始化各耗时区间计数
        const countByCost = [0, 0, 0, 0, 0];
        allNodes.forEach(node => {
            const cost = node.costTime || 0;
            if (cost <= 10) countByCost[0]++;
            else if (cost <= 50) countByCost[1]++;
            else if (cost <= 100) countByCost[2]++;
            else if (cost <= 500) countByCost[3]++;
            else countByCost[4]++;
        });

        // 更新图表
        if (costDistributionChart) {
            costDistributionChart.data.datasets[0].data = countByCost;
            costDistributionChart.update();
        }
    }

    /**
     * 展开所有节点
     */
    function expandAllNodes() {
        document.querySelectorAll('.toggle-btn i.fa-caret-right').forEach(icon => {
            icon.classList.replace('fa-caret-right', 'fa-caret-down');
            icon.closest('.node-item').querySelector('.children-container').classList.remove('hidden');
        });
    }

    /**
     * 折叠所有节点
     */
    function collapseAllNodes() {
        document.querySelectorAll('.toggle-btn i.fa-caret-down').forEach(icon => {
            icon.classList.replace('fa-caret-down', 'fa-caret-right');
            icon.closest('.node-item').querySelector('.children-container').classList.add('hidden');
        });
    }

    /**
     * 全局搜索功能
     */
    function initSearch() {
        dom.globalSearch.addEventListener('input', (e) => {
            const searchText = e.target.value.toLowerCase().trim();
            const allNodes = document.querySelectorAll('.node-item');

            allNodes.forEach(node => {
                const nodeText = node.textContent.toLowerCase();
                const isMatch = searchText ? nodeText.includes(searchText) : true;

                // 显示/隐藏节点
                node.style.display = isMatch ? '' : 'none';

                // 匹配的节点需要确保父节点可见并展开
                if (isMatch) {
                    let parentNode = node.closest('ul').parentNode.closest('.node-item');
                    while (parentNode) {
                        parentNode.style.display = '';
                        // 展开父节点
                        const caretIcon = parentNode.querySelector('.toggle-btn i');
                        if (caretIcon && caretIcon.classList.contains('fa-caret-right')) {
                            caretIcon.classList.replace('fa-caret-right', 'fa-caret-down');
                            parentNode.querySelector('.children-container').classList.remove('hidden');
                        }
                        parentNode = parentNode.closest('ul').parentNode.closest('.node-item');
                    }
                }
            });
        });
    }

    /**
     * 初始化页面（入口函数）
     */
    async function initPage() {
        // 初始化图表
        initChart();
        // 初始化搜索
        initSearch();

        // 加载数据
        const treeData = await fetchTreeData();
        if (treeData) {
            currentTreeData = treeData;
            renderTree(treeData);
        }

        // 绑定按钮事件
        dom.expandAll.addEventListener('click', expandAllNodes);
        dom.collapseAll.addEventListener('click', collapseAllNodes);
        dom.resetView.addEventListener('click', () => {
            if (currentTreeData.length > 0) {
                renderTree(currentTreeData);
                dom.globalSearch.value = ''; // 清空搜索
            }
        });
        dom.refreshBtn.addEventListener('click', async () => {
            const newTreeData = await fetchTreeData();
            if (newTreeData) {
                currentTreeData = newTreeData;
                renderTree(newTreeData);
                dom.globalSearch.value = ''; // 清空搜索
            }
        });
        dom.retryBtn.addEventListener('click', async () => {
            const newTreeData = await fetchTreeData();
            if (newTreeData) {
                currentTreeData = newTreeData;
                renderTree(newTreeData);
            }
        });
        dom.fullscreenBtn.addEventListener('click', () => {
            if (!document.fullscreenElement) {
                document.documentElement.requestFullscreen().catch(err => {
                    console.error('全屏失败:', err);
                });
                dom.fullscreenBtn.innerHTML = '<i class="fa fa-compress text-gray-600"></i>';
            } else {
                if (document.exitFullscreen) {
                    document.exitFullscreen();
                    dom.fullscreenBtn.innerHTML = '<i class="fa fa-expand text-gray-600"></i>';
                }
            }
        });
        // 全屏状态变化监听
        document.addEventListener('fullscreenchange', () => {
            if (document.fullscreenElement) {
                dom.fullscreenBtn.innerHTML = '<i class="fa fa-compress text-gray-600"></i>';
            } else {
                dom.fullscreenBtn.innerHTML = '<i class="fa fa-expand text-gray-600"></i>';
            }
        });
        // 筛选按钮（暂存逻辑，可根据需求扩展）
        dom.applyFilter.addEventListener('click', () => {
            alert('筛选功能已触发，可根据实际需求扩展筛选逻辑');
        });
    }

    // 页面加载完成后初始化
    document.addEventListener('DOMContentLoaded', initPage);
</script>
</body>
</html>